#pragma once

#include "common.h"
#include "Algebra.h"
#include "LfsrRegister.h"
#include "LfsrConfiguration.h"

template<class Category = left_operator_tag, int regSize = 2, int factorsSize = 2>
struct LfsrShifter : public RotatableInvoker<IAlgebra, Category>
{
	typedef RotatableInvoker<IAlgebra, Category> _Multiplier;
	
	void next(
		const IAlgebra *algebra,
		const BYTE *control,
		LfsrRegister<regSize> *reg,
		LfsrRegister<factorsSize> *factors)
	{
		BYTE s = algebra->zeroElement();
		for (int j = 1; j < factorsSize + 1; j++)
		{
			BYTE k = _Multiplier::mult(
				algebra,
				&IAlgebra::mult,
				factors->at(j-1),
				reg->at(control[factorsSize - j + 1]));
			s = algebra->add(s, k);
		}
		reg->rightShift();
		reg->at(regSize - 1) = s;
	}
};


template<class Category, int regSize>
struct LfsrShifter<Category, regSize, 2> : public RotatableInvoker<IAlgebra, Category>
{
	typedef RotatableInvoker<IAlgebra, Category> _Multiplier;

	void next(
		const IAlgebra *algebra,
		const BYTE *control,
		LfsrRegister<regSize> *reg,
		LfsrRegister<2> *factors)
	{
		BYTE s = _Multiplier::op(algebra, &IAlgebra::mult, factors->at(0), reg->at(control[2]));
		BYTE k = _Multiplier::op(algebra, &IAlgebra::mult, factors->at(1), reg->at(control[1]));
		s = algebra->add(s, k);
		reg->rightShift();
		reg->at(regSize - 1) = s;
	}
};

template<class Category = left_operator_tag, int regSize = 2, int factorsSize=2>
class Lfsr : 
	LfsrShifter<Category, regSize, factorsSize>
{
public:
	typedef LfsrShifter<Category, regSize, factorsSize> Shifter;
	typedef LfsrRegister<factorsSize> Factorset;
	typedef LfsrRegister<regSize> State;
	typedef LfsrRegister<factorsSize + 1> ControlSequence;
	typedef LfsrConfiguration<regSize, factorsSize> Configuration;
	typedef LfsrRegisterComparer<regSize> StateComparer;
	typedef LfsrConfigurationComparer<regSize, factorsSize> ConfigurationComparer;

	Lfsr(
		const IAlgebra *algebra,
		const ControlSequence &control,
		const State &reg,
		const Factorset &factors)
		:	_algebra(algebra), 
			_control(control), 
			_reg(reg),
			_factors(factors)
	{
	}

	Lfsr(
		const IAlgebra *algebra,
		const ControlSequence &control)
		:	_algebra(algebra), 
			_control(control),
			_reg(algebra->zeroElement()),
			_factors(algebra->zeroElement())
	{
	}

	typename State next()
	{
		State res = _reg;
		Shifter::next(_algebra, &_control.at(0), &_reg, &_factors);
		return res;
	}
	
	int getRegisterSize() const { return regSize; }

	int getFactorsetSize() const { return factorsSize; }

	void setState(const State &v)
	{
		_reg = v;
	}

	void setFactorset(const Factorset &v)
	{
		_factors = v;
	}

	void setCongiguration(const Configuration &c)
	{
		setState(c.reg);
		setFactorset(c.factors);
	}

	Configuration getConfiguration() const
	{
		return Configuration(_reg, _factors);
	}

private:
	const IAlgebra *_algebra;
	ControlSequence _control;
	State _reg;
	Factorset _factors;
};

template<class Category, int regSize, int factorsSize>	
struct generator_traits<Lfsr<Category, regSize, factorsSize>>
{
	typedef typename Lfsr<Category, regSize, factorsSize> Generator;
	typedef typename Generator::ControlSequence ControlSequence;
	typedef typename Generator::State State;
	typedef typename Generator::Configuration Configuration;
	typedef typename Generator::ConfigurationComparer ConfigurationComparer;
	typedef typename Generator::StateComparer StateComparer;
	static State next(Generator* gen) { return gen->next(); }
	static void setConfiguration(
		Generator* gen,
		const Configuration &c)
	{ 
		gen->setCongiguration(c);
	}
};
